import logging
import gc
from typing import List, Dict, Any
from celery import current_task
from sqlalchemy.orm import Session
# 解析Cron表达式并更新调度
from celery.schedules import crontab

from app.core.celery_app import celery_app
from app.db.session import SessionLocal
from app.crud.shipment import shipment
from app.crud.sys_config import sys_config
from app.crud.api_config import api_config
from app.services.gps_service import GPSApiService
from app.services.shipment_monitor_service import ShipmentMonitorService
from ..utils.cron_utils import convert_quartz_to_apscheduler, parse_cron_expression

logger = logging.getLogger(__name__)


def get_db() -> Session:
    """获取数据库会话"""
    db = SessionLocal()
    try:
        return db
    except Exception as e:
        db.close()
        raise e


@celery_app.task(bind=True, name='app.tasks.shipment_tasks.monitor_shipments_task')
def monitor_shipments_task(self):
    """
    货物监控定时任务
    """
    db = None
    task_id = self.request.id
    logger.info(f"开始执行货物监控任务 {task_id}")
    
    try:
        db = get_db()
        
        # 获取所有需要监控的货物（未开始和进行中状态）
        active_shipments = shipment.get_active_shipments(db)
        
        if not active_shipments:
            logger.info("没有需要监控的货物")
            return {
                'success': True,
                'message': '没有需要监控的货物',
                'processed_count': 0
            }
        
        logger.info(f"找到 {len(active_shipments)} 个需要监控的货物")
        
        # 获取GPS API配置
        gps_api_config = api_config.get_by_code(db, code='gpsApi')
        if not gps_api_config:
            logger.error("未找到GPS API配置")
            return {
                'success': False,
                'message': '未找到GPS API配置',
                'processed_count': 0
            }
        
        # 初始化GPS服务
        gps_service = GPSApiService(
            api_url=gps_api_config.api_url,
            api_user=gps_api_config.api_user,
            api_pwd=gps_api_config.api_pwd
        )
        
        # 登录GPS服务
        login_result = gps_service.login()
        if not login_result['success']:
            logger.error(f"GPS服务登录失败: {login_result['message']}")
            return {
                'success': False,
                'message': f"GPS服务登录失败: {login_result['message']}",
                'processed_count': 0
            }
        
        # 初始化监控服务
        monitor_service = ShipmentMonitorService(db)
        
        # 处理结果统计
        processed_count = 0
        success_count = 0
        completed_shipments = []
        error_messages = []
        
        # 逐个处理货物
        for shipment_obj in active_shipments:
            try:
                logger.info(f"处理货物: {shipment_obj.number_no} (状态: {shipment_obj.status})")
                
                result = monitor_service.process_shipment_tracking(shipment_obj, gps_service)
                processed_count += 1
                
                if result['success']:
                    success_count += 1
                    logger.info(f"货物 {shipment_obj.number_no} 处理成功: {result['message']}")
                    
                    # 如果货物已完成，记录下来
                    if result.get('should_remove'):
                        completed_shipments.append({
                            'id': shipment_obj.id,
                            'number_no': shipment_obj.number_no,
                            'action': result.get('action'),
                            'message': result['message']
                        })
                else:
                    error_msg = f"货物 {shipment_obj.number_no} 处理失败: {result['message']}"
                    logger.error(error_msg)
                    error_messages.append(error_msg)
                
                # 每处理10个货物释放一次内存
                if processed_count % 10 == 0:
                    gc.collect()
                    
            except Exception as e:
                error_msg = f"处理货物 {shipment_obj.number_no} 时发生异常: {str(e)}"
                logger.error(error_msg)
                error_messages.append(error_msg)
                processed_count += 1
        
        # 更新定时任务频率（从数据库读取最新配置）
        update_task_schedule(db)
        
        result = {
            'success': True,
            'task_id': task_id,
            'processed_count': processed_count,
            'success_count': success_count,
            'completed_count': len(completed_shipments),
            'error_count': len(error_messages),
            'completed_shipments': completed_shipments,
            'errors': error_messages[:5] if error_messages else []  # 只返回前5个错误
        }
        
        logger.info(f"货物监控任务 {task_id} 执行完成: 处理 {processed_count} 个，成功 {success_count} 个，完成 {len(completed_shipments)} 个")
        
        return result
        
    except Exception as e:
        error_msg = f"货物监控任务执行失败: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': error_msg,
            'task_id': task_id,
            'processed_count': 0
        }
    finally:
        if db:
            db.close()
        # 强制垃圾回收
        gc.collect()


def update_task_schedule(db: Session):
    """
    更新定时任务调度频率
    """
    try:
        # 从数据库获取任务频率配置
        frequency_config = sys_config.get_value_by_code(db, code='shipmentTaskFrequency', default_value='*/5 * * * *')
        
        new_schedule = _parse_cron_expression(frequency_config)
        # 动态更新调度配置
        celery_app.conf.beat_schedule['monitor-shipments']['schedule'] = new_schedule
        logger.info(f"更新任务调度频率: {frequency_config}")
    except Exception as e:
        logger.warning(f"更新任务调度频率失败: {str(e)}")

def _parse_cron_expression(cron_expression: str):
        """
        解析cron表达式为Celery crontab对象
        
        Args:
            cron_expression: cron表达式
            
        Returns:
            Celery crontab对象
        """
        try:
            # 转换为APScheduler格式
            apscheduler_expression = convert_quartz_to_apscheduler(cron_expression)
            
            # 解析cron表达式
            cron_parts = parse_cron_expression(apscheduler_expression)
            
            # 转换为Celery crontab
            if "second" in cron_parts:
                # 6字段格式 (秒 分 时 日 月 周)
                return crontab(
                    minute=_convert_cron_part(cron_parts["minute"]),
                    hour=_convert_cron_part(cron_parts["hour"]),
                    day_of_month=_convert_cron_part(cron_parts["day"]),
                    month_of_year=_convert_cron_part(cron_parts["month"]),
                    day_of_week=_convert_cron_part(cron_parts["day_of_week"])
                )
            else:
                # 5字段格式 (分 时 日 月 周)
                return crontab(
                    minute=_convert_cron_part(cron_parts["minute"]),
                    hour=_convert_cron_part(cron_parts["hour"]),
                    day_of_month=_convert_cron_part(cron_parts["day"]),
                    month_of_year=_convert_cron_part(cron_parts["month"]),
                    day_of_week=_convert_cron_part(cron_parts["day_of_week"])
                )
        except Exception as e:
            logger.error(f"解析cron表达式 {cron_expression} 失败: {str(e)}", exc_info=True)
            return None
    
def _convert_cron_part(cron_part):
    """
    转换cron表达式的一个部分为Celery可接受的格式
    
    Args:
        cron_part: cron表达式的一部分
        
    Returns:
        Celery可接受的格式
    """
    # 如果是单个值或通配符，直接返回
    if cron_part == '*' or cron_part.isdigit():
        return cron_part
    
    # 处理范围和步长
    if '/' in cron_part:
        base, step = cron_part.split('/')
        if base == '*':
            return f'*/{step}'
        return f'{base}/{step}'
    
    # 处理列表
    if ',' in cron_part:
        return cron_part
    
    # 处理范围
    if '-' in cron_part:
        return cron_part
    
    # 默认返回原始值
    return cron_part



@celery_app.task(bind=True, name='app.tasks.shipment_tasks.process_single_shipment')  
def process_single_shipment_task(self, shipment_id: int):
    """
    处理单个货物的监控任务
    """
    db = None
    task_id = self.request.id
    
    try:
        db = get_db()
        
        # 获取货物信息
        shipment_obj = shipment.get(db, id=shipment_id)
        if not shipment_obj:
            return {
                'success': False,
                'message': f'货物ID {shipment_id} 不存在'
            }
        
        # 检查状态是否需要处理
        if shipment_obj.status not in [0, 10]:
            return {
                'success': True,
                'message': f'货物状态 {shipment_obj.status} 无需处理'
            }
        
        # 获取GPS API配置并处理
        gps_api_config = api_config.get_by_code(db, code='gpsApi')
        if not gps_api_config:
            return {
                'success': False,
                'message': '未找到GPS API配置'
            }
        
        gps_service = GPSApiService(
            api_url=gps_api_config.api_url,
            api_user=gps_api_config.api_user,
            api_pwd=gps_api_config.api_pwd
        )
        
        login_result = gps_service.login()
        if not login_result['success']:
            return {
                'success': False,
                'message': f"GPS服务登录失败: {login_result['message']}"
            }
        
        monitor_service = ShipmentMonitorService(db)
        result = monitor_service.process_shipment_tracking(shipment_obj, gps_service)
        
        logger.info(f"单个货物 {shipment_obj.number_no} 处理结果: {result}")
        return result
        
    except Exception as e:
        error_msg = f"处理货物 {shipment_id} 失败: {str(e)}"
        logger.error(error_msg)
        return {
            'success': False,
            'message': error_msg
        }
    finally:
        if db:
            db.close()